using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Serial;

namespace HslCommunication.Instrument.DLT
{
	/// <summary>
	/// 基于多功能电能表通信协议实现的通讯类，参考的文档是DLT645-2007
	/// </summary>
	public class DLT645 : SerialDeviceBase
	{
		private string station = "1";

		private string password = "00000000";

		private string opCode = "00000000";

		/// <summary>
		/// 指定地址域来实例化一个对象
		/// </summary>
		/// <param name="station">设备的站号信息</param>
		/// <param name="password">密码，写入的时候进行验证的信息</param>
		/// <param name="opCode">操作者代码</param>
		public DLT645(string station, string password = "", string opCode = "")
		{
			base.ByteTransform = new RegularByteTransform();
			this.station = station;
			this.password = (string.IsNullOrEmpty(password) ? "00000000" : password);
			this.opCode = (string.IsNullOrEmpty(opCode) ? "00000000" : opCode);
		}

		/// <summary>
		/// 激活设备的命令，在设备
		/// </summary>
		/// <returns>是否发送成功</returns>
		public OperateResult ActiveDeveice()
		{
			return ReadBase(new byte[4]
			{
				254,
				254,
				254,
				254
			}, sendOnly: true);
		}

		/// <summary>
		/// 地址为数据标识
		/// </summary>
		/// <param name="address">地址信息</param>
		/// <param name="length">数据长度信息</param>
		/// <returns>结果信息</returns>
		public override OperateResult<byte[]> Read(string address, ushort length)
		{
			OperateResult<string, byte[]> operateResult = AnalysisBytesAddress(address, station);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = BuildEntireCommand(operateResult.Content1, 17, operateResult.Content2);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			OperateResult<byte[]> operateResult3 = ReadBase(operateResult2.Content);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			OperateResult operateResult4 = CheckResponse(operateResult3.Content);
			if (!operateResult4.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult4);
			}
			if (operateResult3.Content.Length < 16)
			{
				return OperateResult.CreateSuccessResult(new byte[0]);
			}
			return OperateResult.CreateSuccessResult(operateResult3.Content.SelectMiddle(14, operateResult3.Content.Length - 16));
		}

		/// <inheritdoc />
		public override OperateResult<double[]> ReadDouble(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = Read(address, length);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<double[]>(operateResult);
			}
			return TransDoubleFromDLt(operateResult.Content, length);
		}

		/// <inheritdoc cref="M:HslCommunication.Instrument.DLT.DLT645.ReadDouble(System.String,System.UInt16)" />
		public override async Task<OperateResult<double[]>> ReadDoubleAsync(string address, ushort length)
		{
			return await Task.Run(() => ReadDouble(address, length));
		}

		/// <summary>
		/// 写入数据信息
		/// </summary>
		/// <param name="address">地址信息</param>
		/// <param name="value">写入的数据值</param>
		/// <returns>是否写入成功</returns>
		public override OperateResult Write(string address, byte[] value)
		{
			OperateResult<string, byte[]> operateResult = AnalysisBytesAddress(address, station);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			byte[] dataArea = SoftBasic.SpliceByteArray(operateResult.Content2, password.ToHexBytes(), opCode.ToHexBytes(), value);
			OperateResult<byte[]> operateResult2 = BuildEntireCommand(operateResult.Content1, 21, dataArea);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			OperateResult<byte[]> operateResult3 = ReadBase(operateResult2.Content);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			return CheckResponse(operateResult3.Content);
		}

		/// <summary>
		/// 读取设备的通信地址，仅支持点对点通讯的情况
		/// </summary>
		/// <returns>设备的通信地址</returns>
		public OperateResult<string> ReadAddress()
		{
			OperateResult<byte[]> operateResult = BuildEntireCommand("AAAAAAAAAAAA", 19, null);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<string>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = ReadBase(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<string>(operateResult2);
			}
			OperateResult operateResult3 = CheckResponse(operateResult2.Content);
			if (!operateResult3.IsSuccess)
			{
				return OperateResult.CreateFailedResult<string>(operateResult3);
			}
			return OperateResult.CreateSuccessResult(operateResult2.Content.SelectMiddle(1, 6).ToHexString());
		}

		/// <summary>
		/// 写入设备的通信地址，仅支持点对点通讯的情况
		/// </summary>
		/// <param name="address">等待写入的地址域</param>
		/// <returns>是否写入成功</returns>
		public OperateResult WriteAddress(string address)
		{
			OperateResult<byte[]> addressByteFromString = GetAddressByteFromString(address);
			if (!addressByteFromString.IsSuccess)
			{
				return addressByteFromString;
			}
			OperateResult<byte[]> operateResult = BuildEntireCommand("AAAAAAAAAAAA", 21, addressByteFromString.Content);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			OperateResult<byte[]> operateResult2 = ReadBase(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			OperateResult operateResult3 = CheckResponse(operateResult2.Content);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			if (SoftBasic.IsTwoBytesEquel(operateResult2.Content.SelectMiddle(1, 6), addressByteFromString.Content))
			{
				return OperateResult.CreateSuccessResult();
			}
			return new OperateResult(StringResources.Language.DLTErrorWriteReadCheckFailed);
		}

		/// <summary>
		/// 广播指定的时间，强制从站与主站时间同步
		/// </summary>
		/// <param name="dateTime">时间</param>
		/// <returns>是否成功</returns>
		public OperateResult BroadcastTime(DateTime dateTime)
		{
			string value = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
			OperateResult<byte[]> operateResult = BuildEntireCommand("999999999999", 21, value.ToHexBytes());
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return ReadBase(operateResult.Content, sendOnly: true);
		}

		/// <summary>
		/// 对设备发送冻结命令，默认点对点操作，地址域为99999999999999时为广播，数据域格式说明：MMDDhhmm(月日时分)，
		/// 99DDhhmm表示月为周期定时冻结，9999hhmm表示日为周期定时冻结，999999mm表示以小时为周期定时冻结，99999999表示瞬时冻结
		/// </summary>
		/// <param name="dataArea">数据域信息</param>
		/// <returns>是否成功冻结</returns>
		public OperateResult FreezeCommand(string dataArea)
		{
			OperateResult<string, byte[]> operateResult = AnalysisBytesAddress(dataArea, station);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = BuildEntireCommand(operateResult.Content1, 22, operateResult.Content2);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			if (operateResult.Content1 == "999999999999")
			{
				return ReadBase(operateResult2.Content, sendOnly: true);
			}
			OperateResult<byte[]> operateResult3 = ReadBase(operateResult2.Content);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			return CheckResponse(operateResult3.Content);
		}

		/// <summary>
		/// 更改通信速率，波特率可选600,1200,2400,4800,9600,19200，其他值无效，可以携带地址域信息，s=1;9600
		/// </summary>
		/// <param name="baudRate">波特率的信息</param>
		/// <returns>是否更改成功</returns>
		public OperateResult ChangeBaudRate(string baudRate)
		{
			OperateResult<string, int> operateResult = AnalysisIntegerAddress(baudRate, station);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			byte b = 0;
			switch (operateResult.Content2)
			{
			case 600:
				b = 2;
				break;
			case 1200:
				b = 4;
				break;
			case 2400:
				b = 8;
				break;
			case 4800:
				b = 16;
				break;
			case 9600:
				b = 32;
				break;
			case 19200:
				b = 64;
				break;
			default:
				return new OperateResult(StringResources.Language.NotSupportedFunction);
			}
			OperateResult<byte[]> operateResult2 = BuildEntireCommand(operateResult.Content1, 23, new byte[1]
			{
				b
			});
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			OperateResult<byte[]> operateResult3 = ReadBase(operateResult2.Content);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			OperateResult operateResult4 = CheckResponse(operateResult3.Content);
			if (!operateResult4.IsSuccess)
			{
				return operateResult4;
			}
			if (operateResult3.Content[10] == b)
			{
				return OperateResult.CreateSuccessResult();
			}
			return new OperateResult(StringResources.Language.DLTErrorWriteReadCheckFailed);
		}

		private OperateResult<double[]> TransDoubleFromDLt(byte[] content, ushort length)
		{
			try
			{
				double[] array = new double[length];
				for (int i = 0; i < array.Length; i++)
				{
					byte[] array2 = content.SelectMiddle(i * 4, 4).Reverse().ToArray();
					for (int j = 0; j < array2.Length; j++)
					{
						array2[i] = (byte)(array2[i] - 51);
					}
					array[i] = Convert.ToDouble(array2.ToHexString()) / 100.0;
				}
				return OperateResult.CreateSuccessResult(array);
			}
			catch (Exception ex)
			{
				return new OperateResult<double[]>(ex.Message);
			}
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return $"DLT645[{base.PortName}:{base.BaudRate}]";
		}

		/// <summary>
		/// 将地址解析成BCD码的地址，并且扩充到12位，不够的补0操作
		/// </summary>
		/// <param name="address">地址域信息</param>
		/// <returns>实际的结果</returns>
		public static OperateResult<byte[]> GetAddressByteFromString(string address)
		{
			if (address == null || address.Length == 0)
			{
				return new OperateResult<byte[]>(StringResources.Language.DLTAddressCannotNull);
			}
			if (address.Length > 12)
			{
				return new OperateResult<byte[]>(StringResources.Language.DLTAddressCannotMoreThan12);
			}
			if (!Regex.IsMatch(address, "^[0-9A-A]+$"))
			{
				return new OperateResult<byte[]>(StringResources.Language.DLTAddressMatchFailed);
			}
			if (address.Length < 12)
			{
				address = address.PadLeft(12, '0');
			}
			return OperateResult.CreateSuccessResult(address.ToHexBytes());
		}

		/// <summary>
		/// 将指定的地址信息，控制码信息，数据域信息打包成完整的报文命令
		/// </summary>
		/// <param name="address">地址域信息，地址域由6个字节构成，每字节2位BCD码，地址长度可达12位十进制数。地址域支持锁位寻址，即从若干低位起，剩余高位补AAH作为通配符进行读表操作</param>
		/// <param name="control">控制码信息</param>
		/// <param name="dataArea">数据域的内容</param>
		/// <returns>返回是否报文创建成功</returns>
		public OperateResult<byte[]> BuildEntireCommand(string address, byte control, byte[] dataArea)
		{
			if (dataArea == null)
			{
				dataArea = new byte[0];
			}
			OperateResult<byte[]> addressByteFromString = GetAddressByteFromString(address);
			if (!addressByteFromString.IsSuccess)
			{
				return addressByteFromString;
			}
			byte[] array = new byte[12 + dataArea.Length];
			array[0] = 104;
			addressByteFromString.Content.CopyTo(array, 1);
			array[7] = 104;
			array[8] = control;
			array[9] = (byte)dataArea.Length;
			if (dataArea.Length != 0)
			{
				for (int i = 0; i < dataArea.Length; i++)
				{
					dataArea[i] += 51;
				}
				dataArea.CopyTo(array, 10);
			}
			int num = 0;
			for (int j = 0; j < array.Length - 2; j++)
			{
				num += array[j];
			}
			array[array.Length - 2] = (byte)num;
			array[array.Length - 1] = 22;
			return OperateResult.CreateSuccessResult(array);
		}

		/// <summary>
		/// 从用户输入的地址信息中解析出真实的地址及数据标识
		/// </summary>
		/// <param name="address">用户输入的地址信息</param>
		/// <param name="defalut">默认的地址域</param>
		/// <returns>解析结果信息</returns>
		public static OperateResult<string, byte[]> AnalysisBytesAddress(string address, string defalut)
		{
			string value = defalut;
			byte[] value2 = new byte[4];
			if (address.IndexOf(';') > 0)
			{
				string[] array = address.Split(new char[1]
				{
					';'
				}, StringSplitOptions.RemoveEmptyEntries);
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i].StartsWith("s="))
					{
						value = array[i].Substring(2);
					}
					else
					{
						value2 = array[i].ToHexBytes().Reverse().ToArray();
					}
				}
			}
			else
			{
				value2 = address.ToHexBytes().Reverse().ToArray();
			}
			return OperateResult.CreateSuccessResult(value, value2);
		}

		/// <summary>
		/// 从用户输入的地址信息中解析出真实的地址及数据标识
		/// </summary>
		/// <param name="address">用户输入的地址信息</param>
		/// <param name="defalut">默认的地址域</param>
		/// <returns>解析结果信息</returns>
		public static OperateResult<string, int> AnalysisIntegerAddress(string address, string defalut)
		{
			try
			{
				string value = defalut;
				int value2 = 0;
				if (address.IndexOf(';') > 0)
				{
					string[] array = address.Split(new char[1]
					{
						';'
					}, StringSplitOptions.RemoveEmptyEntries);
					for (int i = 0; i < array.Length; i++)
					{
						if (array[i].StartsWith("s="))
						{
							value = array[i].Substring(2);
						}
						else
						{
							value2 = Convert.ToInt32(array[i]);
						}
					}
				}
				else
				{
					value2 = Convert.ToInt32(address);
				}
				return OperateResult.CreateSuccessResult(value, value2);
			}
			catch (Exception ex)
			{
				return new OperateResult<string, int>(ex.Message);
			}
		}

		/// <summary>
		/// 检查当前的反馈数据信息是否正确
		/// </summary>
		/// <param name="response">从仪表反馈的数据信息</param>
		/// <returns>是否校验成功</returns>
		public static OperateResult CheckResponse(byte[] response)
		{
			if (response.Length < 9)
			{
				return new OperateResult(StringResources.Language.ReceiveDataLengthTooShort);
			}
			if ((response[8] & 0x40) == 64)
			{
				byte value = response[10];
				if (value.GetBoolOnIndex(0))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit0);
				}
				if (value.GetBoolOnIndex(1))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit1);
				}
				if (value.GetBoolOnIndex(2))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit2);
				}
				if (value.GetBoolOnIndex(3))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit3);
				}
				if (value.GetBoolOnIndex(4))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit4);
				}
				if (value.GetBoolOnIndex(5))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit5);
				}
				if (value.GetBoolOnIndex(6))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit6);
				}
				if (value.GetBoolOnIndex(7))
				{
					return new OperateResult(StringResources.Language.DLTErrorInfoBit7);
				}
				return OperateResult.CreateSuccessResult();
			}
			return OperateResult.CreateSuccessResult();
		}
	}
}
